Micron Document
🎖️GitЯра🎖️


Displaying Raw • Download

feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt renovate/actions-setup-java-5.x (f17e586c) Text, 20.51 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.messaging

Tff7b72import T7ee787androidx.compose.foundation.layout.Box
Tff7b72import T7ee787androidx.compose.foundation.layout.Column
Tff7b72import T7ee787androidx.compose.foundation.layout.PaddingValues
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxSize
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxWidth
Tff7b72import T7ee787androidx.compose.foundation.layout.padding
Tff7b72import T7ee787androidx.compose.foundation.lazy.LazyColumn
Tff7b72import T7ee787androidx.compose.foundation.lazy.LazyListState
Tff7b72import T7ee787androidx.compose.foundation.lazy.rememberLazyListState
Tff7b72import T7ee787androidx.compose.material3.CircularProgressIndicator
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.LaunchedEffect
Tff7b72import T7ee787androidx.compose.runtime.MutableState
Tff7b72import T7ee787androidx.compose.runtime.derivedStateOf
Tff7b72import T7ee787androidx.compose.runtime.getValue
Tff7b72import T7ee787androidx.compose.runtime.mutableStateOf
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.runtime.rememberCoroutineScope
Tff7b72import T7ee787androidx.compose.runtime.rememberUpdatedState
Tff7b72import T7ee787androidx.compose.runtime.setValue
Tff7b72import T7ee787androidx.compose.runtime.snapshotFlow
Tff7b72import T7ee787androidx.compose.ui.Alignment
Tff7b72import T7ee787androidx.compose.ui.Modifier
Tff7b72import T7ee787androidx.compose.ui.hapticfeedback.HapticFeedback
Tff7b72import T7ee787androidx.compose.ui.hapticfeedback.HapticFeedbackType
Tff7b72import T7ee787androidx.compose.ui.platform.LocalHapticFeedback
Tff7b72import T7ee787androidx.compose.ui.unit.dp
Tff7b72import T7ee787androidx.lifecycle.compose.LifecycleResumeEffect
Tff7b72import T7ee787androidx.paging.LoadState
Tff7b72import T7ee787androidx.paging.compose.LazyPagingItems
Tff7b72import T7ee787androidx.paging.compose.itemContentType
Tff7b72import T7ee787androidx.paging.compose.itemKey
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.FlowPreview
Tff7b72import T7ee787kotlinx.coroutines.flow.collectLatest
Tff7b72import T7ee787kotlinx.coroutines.flow.debounce
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787org.meshtastic.core.model.Message
Tff7b72import T7ee787org.meshtastic.core.model.MessageStatus
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.Reaction
Tff7b72import T7ee787org.meshtastic.feature.messaging.component.MessageItem
Tff7b72import T7ee787org.meshtastic.feature.messaging.component.MessageStatusDialog
Tff7b72import T7ee787org.meshtastic.feature.messaging.component.ReactionDialog
Tff7b72import T7ee787org.meshtastic.feature.messaging.component.UnreadMessagesDivider

Tff7b72internal Tff7b72data Tff7b72class T56d364MessageListHandlersTb4b4b4(
Tff7b72val Te6edf3onUnreadChangedTb4b4b4: Tb4b4b4(Tffa657LongTb4b4b4, Tffa657LongTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tff7b72val Te6edf3onSendReactionTb4b4b4: Tb4b4b4(Tffa657StringTb4b4b4, Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tff7b72val Te6edf3onClickChipTb4b4b4: Tb4b4b4(Te6edf3NodeTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tff7b72val Te6edf3onDeleteMessagesTb4b4b4: Tb4b4b4(Te6edf3ListTff7b72<Tffa657LongTff7b72>Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tff7b72val Te6edf3onSendMessageTb4b4b4: Tb4b4b4(Tffa657StringTb4b4b4, Tffa657StringTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tff7b72val Te6edf3onReplyTb4b4b4: Tb4b4b4(Te6edf3Message?Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)

Tff7b72internal Tff7b72data Tff7b72class T56d364MessageListPagedStateTb4b4b4(
Tff7b72val Te6edf3nodesTb4b4b4: Te6edf3ListTff7b72<Te6edf3NodeTff7b72>Tb4b4b4,
Tff7b72val Te6edf3ourNodeTb4b4b4: Te6edf3Node?Tb4b4b4,
Tff7b72val Te6edf3messagesTb4b4b4: Te6edf3LazyPagingItemsTff7b72<Te6edf3MessageTff7b72>Tb4b4b4,
Tff7b72val Te6edf3selectedIdsTb4b4b4: Te6edf3MutableStateTff7b72<Te6edf3SetTff7b72<Tffa657LongTff7b72>Tff7b72>Tb4b4b4,
Tff7b72val Te6edf3contactKeyTb4b4b4: Tffa657StringTb4b4b4,
Tff7b72val Te6edf3firstUnreadMessageUuidTb4b4b4: Tffa657Long? Tff7b72= Tff7b72nullTb4b4b4,
Tff7b72val Te6edf3hasUnreadMessagesTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4,
Tff7b72val Te6edf3filteredCountTb4b4b4: Tffa657Int Tff7b72= T79c0ff0Tb4b4b4,
Tff7b72val Te6edf3showFilteredTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4,
Tff7b72val Te6edf3filteringDisabledTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72fun Td2a8ffMutableStateTff7b72<Te6edf3SetTff7b72<Tffa657LongTff7b72>Tff7b72>Tb4b4b4.Te6edf3toggleTb4b4b4(Te6edf3uuidTb4b4b4: Tffa657LongTb4b4b4) Tb4b4b4{
Te6edf3value Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3valueTb4b4b4.Te6edf3containsTb4b4b4(Te6edf3uuidTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3value Tff7b72- Te6edf3uuid
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3value Tff7b72+ Te6edf3uuid
Tb4b4b4}
Tb4b4b4}

Tf0883e@Composable
Tff7b72internal Tff7b72fun Td2a8ffMessageListPagedTb4b4b4(
Te6edf3stateTb4b4b4: Te6edf3MessageListPagedStateTb4b4b4,
Te6edf3handlersTb4b4b4: Te6edf3MessageListHandlersTb4b4b4,
Te6edf3modifierTb4b4b4: Te6edf3Modifier Tff7b72= Te6edf3ModifierTb4b4b4,
Te6edf3listStateTb4b4b4: Te6edf3LazyListState Tff7b72= Te6edf3rememberLazyListStateTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3quickEmojisTb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72> Tff7b72= Te6edf3emptyListTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3haptics Tff7b72= Te6edf3LocalHapticFeedbackTb4b4b4.Te6edf3current
Tff7b72val Te6edf3inSelectionMode Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3derivedStateOf Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4) Tb4b4b4} Tb4b4b4}

T8b949e// Optimization: Pre-calculate map for O(1) lookup in list items to avoid O(N) linear search during scrolling.
Tff7b72val Te6edf3nodeMap Tff7b72= Te6edf3rememberTb4b4b4(Te6edf3stateTb4b4b4.Te6edf3nodesTb4b4b4) Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3nodesTb4b4b4.Te6edf3associateBy Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3num Tb4b4b4} Tb4b4b4}

Tff7b72var Te6edf3showStatusDialog Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTff7b72<Te6edf3Message?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3showStatusDialogTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3message Tff7b72-Tff7b72>
Te6edf3MessageStatusDialogTb4b4b4(
Te6edf3message Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3resendOption Tff7b72= Te6edf3messageTb4b4b4.Te6edf3statusTff7b72?.Te6edf3equalsTb4b4b4(Te6edf3MessageStatusTb4b4b4.Te6edf3ERRORTb4b4b4) Tff7b72?: Tff7b72falseTb4b4b4,
Te6edf3onResend Tff7b72= Tb4b4b4{
Te6edf3handlersTb4b4b4.Te6edf3onDeleteMessagesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4)Tb4b4b4)
Te6edf3handlersTb4b4b4.Te6edf3onSendMessageTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3textTb4b4b4, Te6edf3stateTb4b4b4.Te6edf3contactKeyTb4b4b4)
Te6edf3showStatusDialog Tff7b72= Tff7b72null
Tb4b4b4}Tb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{ Te6edf3showStatusDialog Tff7b72= Tff7b72null Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72var Te6edf3showReactionDialog Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTff7b72<Te6edf3ListTff7b72<Te6edf3ReactionTff7b72>Tff7b72?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3showReactionDialogTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3reactions Tff7b72-Tff7b72>
Te6edf3ReactionDialogTb4b4b4(
Te6edf3reactions Tff7b72= Te6edf3reactionsTb4b4b4,
Te6edf3myId Tff7b72= Te6edf3stateTb4b4b4.Te6edf3ourNodeTff7b72?.Te6edf3userTff7b72?.Te6edf3idTb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{ Te6edf3showReactionDialog Tff7b72= Tff7b72null Tb4b4b4}Tb4b4b4,
Te6edf3onResend Tff7b72= Tb4b4b4{ Te6edf3reaction Tff7b72-Tff7b72>
Te6edf3handlersTb4b4b4.Te6edf3onSendReactionTb4b4b4(Te6edf3reactionTb4b4b4.Te6edf3emojiTb4b4b4, Te6edf3reactionTb4b4b4.Te6edf3replyIdTb4b4b4)
Te6edf3showReactionDialog Tff7b72= Tff7b72null
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3coroutineScope Tff7b72= Te6edf3rememberCoroutineScopeTb4b4b4(Tb4b4b4)

T8b949e// Disable auto-scroll when any dialog is open to prevent list jumping
Tff7b72val Te6edf3hasDialogOpen Tff7b72= Te6edf3showStatusDialog Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72| Te6edf3showReactionDialog Tff7b72!Tff7b72= Tff7b72null

T8b949e// Track unread count based on scroll position
Te6edf3UpdateUnreadCountPagedTb4b4b4(Te6edf3listState Tff7b72= Te6edf3listStateTb4b4b4, Te6edf3messages Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4, Te6edf3onUnreadChange Tff7b72= Te6edf3handlersTb4b4b4.Te6edf3onUnreadChangedTb4b4b4)

T8b949e// Auto-scroll to bottom when new messages arrive
Te6edf3AutoScrollToBottomPagedTb4b4b4(
Te6edf3listState Tff7b72= Te6edf3listStateTb4b4b4,
Te6edf3messages Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4,
Te6edf3hasUnreadMessages Tff7b72= Te6edf3stateTb4b4b4.Te6edf3hasUnreadMessagesTb4b4b4,
Te6edf3hasDialogOpen Tff7b72= Te6edf3hasDialogOpenTb4b4b4,
Tb4b4b4)

Te6edf3MessageListPagedContentTb4b4b4(
Te6edf3listState Tff7b72= Te6edf3listStateTb4b4b4,
Te6edf3state Tff7b72= Te6edf3stateTb4b4b4,
Te6edf3nodeMap Tff7b72= Te6edf3nodeMapTb4b4b4,
Te6edf3handlers Tff7b72= Te6edf3handlersTb4b4b4,
Te6edf3inSelectionMode Tff7b72= Te6edf3inSelectionModeTb4b4b4,
Te6edf3coroutineScope Tff7b72= Te6edf3coroutineScopeTb4b4b4,
Te6edf3haptics Tff7b72= Te6edf3hapticsTb4b4b4,
Te6edf3onShowStatusDialog Tff7b72= Tb4b4b4{ Te6edf3showStatusDialog Tff7b72= Tffa657it Tb4b4b4}Tb4b4b4,
Te6edf3onShowReactions Tff7b72= Tb4b4b4{ Te6edf3showReactionDialog Tff7b72= Tffa657it Tb4b4b4}Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3modifierTb4b4b4,
Te6edf3quickEmojis Tff7b72= Te6edf3quickEmojisTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffMessageListPagedContentTb4b4b4(
Te6edf3listStateTb4b4b4: Te6edf3LazyListStateTb4b4b4,
Te6edf3stateTb4b4b4: Te6edf3MessageListPagedStateTb4b4b4,
Te6edf3nodeMapTb4b4b4: Te6edf3MapTff7b72<Tffa657IntTb4b4b4, Te6edf3NodeTff7b72>Tb4b4b4,
Te6edf3handlersTb4b4b4: Te6edf3MessageListHandlersTb4b4b4,
Te6edf3inSelectionModeTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3coroutineScopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4,
Te6edf3hapticsTb4b4b4: Te6edf3HapticFeedbackTb4b4b4,
Te6edf3onShowStatusDialogTb4b4b4: Tb4b4b4(Te6edf3MessageTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onShowReactionsTb4b4b4: Tb4b4b4(Te6edf3ListTff7b72<Te6edf3ReactionTff7b72>Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3modifierTb4b4b4: Te6edf3Modifier Tff7b72= Te6edf3ModifierTb4b4b4,
Te6edf3quickEmojisTb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72>Tb4b4b4,
Tb4b4b4) Tb4b4b4{
T8b949e// Calculate unread divider position using snapshot to avoid side-effects and improve performance
T8b949e// Optimized: Use full snapshot index to correctly match LazyColumn index range
Tff7b72val Te6edf3unreadDividerIndex Tff7b72by
Te6edf3rememberTb4b4b4(Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemCountTb4b4b4, Te6edf3stateTb4b4b4.Te6edf3firstUnreadMessageUuidTb4b4b4) Tb4b4b4{
Te6edf3derivedStateOf Tb4b4b4{
Tff7b72val Te6edf3uuid Tff7b72= Te6edf3stateTb4b4b4.Te6edf3firstUnreadMessageUuid Tff7b72?: Tff7b72returnTf0883e@derivedStateOf Tff7b72null
Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemSnapshotListTb4b4b4.Te6edf3indexOfFirst Tb4b4b4{ Tffa657itTff7b72?.Te6edf3uuid Tff7b72=Tff7b72= Te6edf3uuid Tb4b4b4}Tb4b4b4.Te6edf3takeIf Tb4b4b4{ Tffa657it Tff7b72!Tff7b72= Tff7b72-T79c0ff1 Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e// Disable animations during scroll to prevent jank/stutter
Tff7b72val Te6edf3enableAnimations Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3derivedStateOf Tb4b4b4{ Tff7b72!Te6edf3listStateTb4b4b4.Te6edf3isScrollInProgress Tb4b4b4} Tb4b4b4}

Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3modifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LazyColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3state Tff7b72= Te6edf3listStateTb4b4b4,
Te6edf3reverseLayout Tff7b72= Tff7b72trueTb4b4b4,
Te6edf3contentPadding Tff7b72= Te6edf3PaddingValuesTb4b4b4(Te6edf3bottom Tff7b72= T79c0ff2T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3itemsTb4b4b4(
Te6edf3count Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemCountTb4b4b4,
Te6edf3key Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemKey Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3uuid Tb4b4b4}Tb4b4b4,
Te6edf3contentType Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemContentType Tb4b4b4{ Ta5d6ff"Ta5d6ffmessageTa5d6ff" Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3index Tff7b72-Tff7b72>
Tff7b72val Te6edf3message Tff7b72= Te6edf3stateTb4b4b4.Te6edf3messagesTff7b72[Te6edf3indexTff7b72]
Tff7b72val Te6edf3visuallyPrevMessage Tff7b72= Tff7b72if Tb4b4b4(Te6edf3index Tff7b72< Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemCount Tff7b72- T79c0ff1Tb4b4b4) Te6edf3stateTb4b4b4.Te6edf3messagesTff7b72[Te6edf3index Tff7b72+ T79c0ff1Tff7b72] Tff7b72else Tff7b72null
Tff7b72val Te6edf3visuallyNextMessage Tff7b72= Tff7b72if Tb4b4b4(Te6edf3index Tff7b72> T79c0ff0Tb4b4b4) Te6edf3stateTb4b4b4.Te6edf3messagesTff7b72[Te6edf3index Tff7b72- T79c0ff1Tff7b72] Tff7b72else Tff7b72null

Tff7b72val Te6edf3hasSamePrev Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3message Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3visuallyPrevMessage Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3visuallyPrevMessageTb4b4b4.Te6edf3fromLocal Tff7b72=Tff7b72= Te6edf3messageTb4b4b4.Te6edf3fromLocal Tff7b72&Tff7b72&
Tb4b4b4(Te6edf3messageTb4b4b4.Te6edf3fromLocal Tff7b72|Tff7b72| Te6edf3visuallyPrevMessageTb4b4b4.Te6edf3nodeTb4b4b4.Te6edf3num Tff7b72=Tff7b72= Te6edf3messageTb4b4b4.Te6edf3nodeTb4b4b4.Te6edf3numTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72false
Tb4b4b4}

Tff7b72val Te6edf3hasSameNext Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3message Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3visuallyNextMessage Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3visuallyNextMessageTb4b4b4.Te6edf3fromLocal Tff7b72=Tff7b72= Te6edf3messageTb4b4b4.Te6edf3fromLocal Tff7b72&Tff7b72&
Tb4b4b4(Te6edf3messageTb4b4b4.Te6edf3fromLocal Tff7b72|Tff7b72| Te6edf3visuallyNextMessageTb4b4b4.Te6edf3nodeTb4b4b4.Te6edf3num Tff7b72=Tff7b72= Te6edf3messageTb4b4b4.Te6edf3nodeTb4b4b4.Te6edf3numTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72false
Tb4b4b4}

Tff7b72if Tb4b4b4(Te6edf3message Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3isFirstUnread Tff7b72= Te6edf3stateTb4b4b4.Te6edf3hasUnreadMessages Tff7b72&Tff7b72& Te6edf3unreadDividerIndex Tff7b72=Tff7b72= Te6edf3index
Tff7b72val Te6edf3itemModifier Tff7b72= Tff7b72if Tb4b4b4(Te6edf3enableAnimationsTb4b4b4) Te6edf3ModifierTb4b4b4.Te6edf3animateItemTb4b4b4(Tb4b4b4) Tff7b72else Te6edf3Modifier

Tff7b72if Tb4b4b4(Te6edf3isFirstUnreadTb4b4b4) Tb4b4b4{
T8b949e// Wrap in Column to prevent overlapping of divider and message item
T8b949e// Apply animation to the container Column once
Te6edf3ColumnTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3itemModifierTb4b4b4) Tb4b4b4{
Te6edf3UnreadMessagesDividerTb4b4b4(Tb4b4b4)
Te6edf3RenderPagedChatMessageRowTb4b4b4(
Te6edf3message Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3state Tff7b72= Te6edf3stateTb4b4b4,
Te6edf3nodeMap Tff7b72= Te6edf3nodeMapTb4b4b4,
Te6edf3handlers Tff7b72= Te6edf3handlersTb4b4b4,
Te6edf3inSelectionMode Tff7b72= Te6edf3inSelectionModeTb4b4b4,
Te6edf3coroutineScope Tff7b72= Te6edf3coroutineScopeTb4b4b4,
Te6edf3haptics Tff7b72= Te6edf3hapticsTb4b4b4,
Te6edf3listState Tff7b72= Te6edf3listStateTb4b4b4,
Te6edf3onShowStatusDialog Tff7b72= Te6edf3onShowStatusDialogTb4b4b4,
Te6edf3onShowReactions Tff7b72= Te6edf3onShowReactionsTb4b4b4,
Te6edf3showUserName Tff7b72= Tff7b72!Te6edf3hasSamePrevTb4b4b4,
Te6edf3hasSamePrev Tff7b72= Te6edf3hasSamePrevTb4b4b4,
Te6edf3hasSameNext Tff7b72= Te6edf3hasSameNextTb4b4b4,
Te6edf3quickEmojis Tff7b72= Te6edf3quickEmojisTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3RenderPagedChatMessageRowTb4b4b4(
Te6edf3message Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3state Tff7b72= Te6edf3stateTb4b4b4,
Te6edf3nodeMap Tff7b72= Te6edf3nodeMapTb4b4b4,
Te6edf3handlers Tff7b72= Te6edf3handlersTb4b4b4,
Te6edf3inSelectionMode Tff7b72= Te6edf3inSelectionModeTb4b4b4,
Te6edf3coroutineScope Tff7b72= Te6edf3coroutineScopeTb4b4b4,
Te6edf3haptics Tff7b72= Te6edf3hapticsTb4b4b4,
Te6edf3listState Tff7b72= Te6edf3listStateTb4b4b4,
Te6edf3onShowStatusDialog Tff7b72= Te6edf3onShowStatusDialogTb4b4b4,
Te6edf3onShowReactions Tff7b72= Te6edf3onShowReactionsTb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3itemModifierTb4b4b4,
Te6edf3showUserName Tff7b72= Tff7b72!Te6edf3hasSamePrevTb4b4b4,
Te6edf3hasSamePrev Tff7b72= Te6edf3hasSamePrevTb4b4b4,
Te6edf3hasSameNext Tff7b72= Te6edf3hasSameNextTb4b4b4,
Te6edf3quickEmojis Tff7b72= Te6edf3quickEmojisTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e// Loading indicator at the end (top when reversed) when loading more items
Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3apply Tb4b4b4{
Tff7b72when Tb4b4b4{
Te6edf3loadStateTb4b4b4.Te6edf3append Tff7b72is Te6edf3LoadStateTb4b4b4.Te6edf3Loading Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3itemTb4b4b4(Te6edf3key Tff7b72= Ta5d6ff"Ta5d6ffappend_loadingTa5d6ff"Tb4b4b4, Te6edf3contentType Tff7b72= Ta5d6ff"Ta5d6ffloadingTa5d6ff"Tb4b4b4) Tb4b4b4{
Te6edf3BoxTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3contentAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3CircularProgressIndicatorTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongParameterListTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffRenderPagedChatMessageRowTb4b4b4(
Te6edf3messageTb4b4b4: Te6edf3MessageTb4b4b4,
Te6edf3stateTb4b4b4: Te6edf3MessageListPagedStateTb4b4b4,
Te6edf3nodeMapTb4b4b4: Te6edf3MapTff7b72<Tffa657IntTb4b4b4, Te6edf3NodeTff7b72>Tb4b4b4,
Te6edf3handlersTb4b4b4: Te6edf3MessageListHandlersTb4b4b4,
Te6edf3inSelectionModeTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3coroutineScopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4,
Te6edf3hapticsTb4b4b4: Te6edf3HapticFeedbackTb4b4b4,
Te6edf3listStateTb4b4b4: Te6edf3LazyListStateTb4b4b4,
Te6edf3onShowStatusDialogTb4b4b4: Tb4b4b4(Te6edf3MessageTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onShowReactionsTb4b4b4: Tb4b4b4(Te6edf3ListTff7b72<Te6edf3ReactionTff7b72>Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3modifierTb4b4b4: Te6edf3Modifier Tff7b72= Te6edf3ModifierTb4b4b4,
Te6edf3showUserNameTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3hasSamePrevTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3hasSameNextTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3quickEmojisTb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72>Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3ourNode Tff7b72= Te6edf3stateTb4b4b4.Te6edf3ourNode Tff7b72?: Tff7b72return
Tff7b72val Te6edf3selected Tff7b72by
Te6edf3rememberTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4, Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3valueTb4b4b4) Tb4b4b4{
Te6edf3derivedStateOf Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3containsTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3node Tff7b72= Te6edf3nodeMapTff7b72[Te6edf3messageTb4b4b4.Te6edf3nodeTb4b4b4.Te6edf3numTff7b72] Tff7b72?: Te6edf3messageTb4b4b4.Te6edf3node

Te6edf3MessageItemTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3modifierTb4b4b4,
Te6edf3node Tff7b72= Te6edf3nodeTb4b4b4,
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3message Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3selected Tff7b72= Te6edf3selectedTb4b4b4,
Te6edf3inSelectionMode Tff7b72= Te6edf3inSelectionModeTb4b4b4,
Te6edf3onClick Tff7b72= Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3inSelectionModeTb4b4b4) Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3toggleTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onLongClick Tff7b72= Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3inSelectionModeTb4b4b4) Tb4b4b4{
Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3toggleTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4)
Tb4b4b4}
Te6edf3hapticsTb4b4b4.Te6edf3performHapticFeedbackTb4b4b4(Te6edf3HapticFeedbackTypeTb4b4b4.Te6edf3LongPressTb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onSelect Tff7b72= Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3selectedIdsTb4b4b4.Te6edf3toggleTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onDelete Tff7b72= Tb4b4b4{ Te6edf3handlersTb4b4b4.Te6edf3onDeleteMessagesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3uuidTb4b4b4)Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onClickChip Tff7b72= Te6edf3handlersTb4b4b4.Te6edf3onClickChipTb4b4b4,
Te6edf3onStatusClick Tff7b72= Tb4b4b4{ Te6edf3onShowStatusDialogTb4b4b4(Te6edf3messageTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onReply Tff7b72= Tb4b4b4{ Te6edf3handlersTb4b4b4.Te6edf3onReplyTb4b4b4(Te6edf3messageTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3emojis Tff7b72= Te6edf3messageTb4b4b4.Te6edf3emojisTb4b4b4,
Te6edf3showUserName Tff7b72= Te6edf3showUserNameTb4b4b4,
Te6edf3sendReaction Tff7b72= Tb4b4b4{ Te6edf3emoji Tff7b72-Tff7b72>
Tff7b72val Te6edf3hasReacted Tff7b72=
Te6edf3messageTb4b4b4.Te6edf3emojisTb4b4b4.Te6edf3any Tb4b4b4{ Te6edf3reaction Tff7b72-Tff7b72>
Tb4b4b4(
Te6edf3reactionTb4b4b4.Te6edf3userTb4b4b4.Te6edf3id Tff7b72=Tff7b72= Te6edf3ourNodeTb4b4b4.Te6edf3userTb4b4b4.Te6edf3id Tff7b72|Tff7b72|
Te6edf3reactionTb4b4b4.Te6edf3userTb4b4b4.Te6edf3id Tff7b72=Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3modelTb4b4b4.Te6edf3DataPacketTb4b4b4.Te6edf3ID_LOCAL
Tb4b4b4) Tff7b72&Tff7b72& Te6edf3reactionTb4b4b4.Te6edf3emoji Tff7b72=Tff7b72= Te6edf3emoji
Tb4b4b4}
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3hasReactedTb4b4b4) Tb4b4b4{
Te6edf3handlersTb4b4b4.Te6edf3onSendReactionTb4b4b4(Te6edf3emojiTb4b4b4, Te6edf3messageTb4b4b4.Te6edf3packetIdTb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onShowReactions Tff7b72= Tb4b4b4{ Te6edf3onShowReactionsTb4b4b4(Te6edf3messageTb4b4b4.Te6edf3emojisTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onNavigateToOriginalMessage Tff7b72= Tb4b4b4{
Te6edf3coroutineScopeTb4b4b4.Te6edf3launch Tb4b4b4{
T8b949e// Note: With pagination, we can't guarantee the original message is loaded
T8b949e// Optimized: Use snapshot to find index to avoid side-effects during search
Tff7b72val Te6edf3targetIndex Tff7b72=
Te6edf3stateTb4b4b4.Te6edf3messagesTb4b4b4.Te6edf3itemSnapshotListTb4b4b4.Te6edf3indexOfFirst Tb4b4b4{ Tffa657itTff7b72?.Te6edf3packetId Tff7b72=Tff7b72= Te6edf3messageTb4b4b4.Te6edf3replyId Tb4b4b4}Tb4b4b4.Te6edf3takeIf Tb4b4b4{ Tffa657it Tff7b72!Tff7b72= Tff7b72-T79c0ff1 Tb4b4b4}

Tff7b72if Tb4b4b4(Te6edf3targetIndex Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3listStateTb4b4b4.Te6edf3animateScrollToItemTb4b4b4(Te6edf3index Tff7b72= Te6edf3targetIndexTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3hasSamePrev Tff7b72= Te6edf3hasSamePrevTb4b4b4,
Te6edf3hasSameNext Tff7b72= Te6edf3hasSameNextTb4b4b4,
Te6edf3quickEmojis Tff7b72= Te6edf3quickEmojisTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffAutoScrollToBottomPagedTb4b4b4(
Te6edf3listStateTb4b4b4: Te6edf3LazyListStateTb4b4b4,
Te6edf3messagesTb4b4b4: Te6edf3LazyPagingItemsTff7b72<Te6edf3MessageTff7b72>Tb4b4b4,
Te6edf3hasUnreadMessagesTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3hasDialogOpenTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3itemThresholdTb4b4b4: Tffa657Int Tff7b72= T79c0ff3Tb4b4b4,
Tb4b4b4) Tff7b72= Te6edf3withTb4b4b4(Te6edf3listStateTb4b4b4) Tb4b4b4{
T8b949e// Cache whether we were at the bottom - only update when not actively scrolling
T8b949e// This prevents stuttering while still tracking position for auto-scroll
Tff7b72var Te6edf3cachedAtBottom Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72trueTb4b4b4) Tb4b4b4}

Tff7b72val Te6edf3isCurrentlyAtBottom Tff7b72by
Te6edf3rememberTb4b4b4(Te6edf3hasUnreadMessagesTb4b4b4, Te6edf3hasDialogOpenTb4b4b4) Tb4b4b4{
Te6edf3derivedStateOf Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3hasDialogOpenTb4b4b4) Tb4b4b4{
Tff7b72false
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3isAtBottom Tff7b72=
Te6edf3firstVisibleItemIndex Tff7b72=Tff7b72= T79c0ff0 Tff7b72&Tff7b72&
Te6edf3firstVisibleItemScrollOffset Tff7b72<Tff7b72= Te6edf3UnreadUiDefaultsTb4b4b4.Te6edf3AUTO_SCROLL_BOTTOM_OFFSET_TOLERANCE
Tff7b72val Te6edf3isNearBottom Tff7b72= Te6edf3firstVisibleItemIndex Tff7b72<Tff7b72= Te6edf3itemThreshold
Te6edf3isAtBottom Tff7b72|Tff7b72| Tb4b4b4(Tff7b72!Te6edf3hasUnreadMessages Tff7b72&Tff7b72& Te6edf3isNearBottomTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e// Update cached position only when scroll is idle to prevent stuttering
Te6edf3LaunchedEffectTb4b4b4(Te6edf3isScrollInProgressTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isScrollInProgressTb4b4b4) Tb4b4b4{
Te6edf3cachedAtBottom Tff7b72= Te6edf3isCurrentlyAtBottom
Tb4b4b4}
Tb4b4b4}

T8b949e// Consolidated scroll logic to prevent race conditions
T8b949e// Fixes issue where multiple scroll operations could trigger simultaneously
T8b949e// by unifying all scroll triggers into a single LaunchedEffect
Te6edf3LaunchedEffectTb4b4b4(Te6edf3messagesTb4b4b4.Te6edf3itemCountTb4b4b4) Tb4b4b4{
T8b949e// Use cached position (captured when scroll was idle) to decide if we should auto-scroll
T8b949e// This prevents race conditions where new message renders before we check position
Tff7b72if Tb4b4b4(Te6edf3cachedAtBottom Tff7b72&Tff7b72& Te6edf3messagesTb4b4b4.Te6edf3itemCount Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3scrollToItemTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e// Update cache immediately after scrolling
Te6edf3cachedAtBottom Tff7b72= Tff7b72true
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8fffindFirstVisibleUnreadMessageTb4b4b4(Te6edf3messagesTb4b4b4: Te6edf3LazyPagingItemsTff7b72<Te6edf3MessageTff7b72>Tb4b4b4, Te6edf3visibleIndexTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3Message? Tb4b4b4{
Tff7b72val Te6edf3snapshot Tff7b72= Te6edf3messagesTb4b4b4.Te6edf3itemSnapshotList
Tff7b72if Tb4b4b4(Te6edf3visibleIndex Tff7b72>Tff7b72= Te6edf3snapshotTb4b4b4.Te6edf3sizeTb4b4b4) Tff7b72return Tff7b72null
Tff7b72val Te6edf3firstVisibleUnreadIndex Tff7b72=
Tb4b4b4(Te6edf3visibleIndex Te6edf3until Te6edf3snapshotTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4.Te6edf3firstOrNull Tb4b4b4{ Te6edf3i Tff7b72-Tff7b72>
Tff7b72val Te6edf3msg Tff7b72= Te6edf3snapshotTff7b72[Te6edf3iTff7b72]
Te6edf3msg Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Tff7b72!Te6edf3msgTb4b4b4.Te6edf3read Tff7b72&Tff7b72& Tff7b72!Te6edf3msgTb4b4b4.Te6edf3fromLocal
Tb4b4b4}
Tff7b72return Te6edf3firstVisibleUnreadIndexTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3snapshotTff7b72[Tffa657itTff7b72] Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8fffindLastUnreadMessageIndexTb4b4b4(Te6edf3messagesTb4b4b4: Te6edf3LazyPagingItemsTff7b72<Te6edf3MessageTff7b72>Tb4b4b4)Tb4b4b4: Tffa657Int? Tb4b4b4{
Tff7b72val Te6edf3snapshot Tff7b72= Te6edf3messagesTb4b4b4.Te6edf3itemSnapshotList
Tff7b72return Tb4b4b4(T79c0ff0 Te6edf3until Te6edf3snapshotTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4.Te6edf3lastOrNull Tb4b4b4{ Te6edf3i Tff7b72-Tff7b72>
Tff7b72val Te6edf3msg Tff7b72= Te6edf3snapshotTff7b72[Te6edf3iTff7b72]
Te6edf3msg Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Tff7b72!Te6edf3msgTb4b4b4.Te6edf3read Tff7b72&Tff7b72& Tff7b72!Te6edf3msgTb4b4b4.Te6edf3fromLocal
Tb4b4b4}
Tb4b4b4}

Tf0883e@OptInTb4b4b4(Te6edf3FlowPreviewTff7b72::Te6edf3classTb4b4b4)
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffUpdateUnreadCountPagedTb4b4b4(
Te6edf3listStateTb4b4b4: Te6edf3LazyListStateTb4b4b4,
Te6edf3messagesTb4b4b4: Te6edf3LazyPagingItemsTff7b72<Te6edf3MessageTff7b72>Tb4b4b4,
Te6edf3onUnreadChangeTb4b4b4: Tb4b4b4(Tffa657LongTb4b4b4, Tffa657LongTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3currentOnUnreadChange Tff7b72by Te6edf3rememberUpdatedStateTb4b4b4(Te6edf3onUnreadChangeTb4b4b4)
Tff7b72var Te6edf3isResumed Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72falseTb4b4b4) Tb4b4b4}

T8b949e// Track lifecycle state changes
Te6edf3LifecycleResumeEffectTb4b4b4(Tffa657UnitTb4b4b4) Tb4b4b4{
Te6edf3isResumed Tff7b72= Tff7b72true
Te6edf3onPauseOrDispose Tb4b4b4{ Te6edf3isResumed Tff7b72= Tff7b72false Tb4b4b4}
Tb4b4b4}

T8b949e// Track remote message count to restart effect when remote messages change
T8b949e// This fixes race condition when sending/receiving messages during debounce period
T8b949e// Optimized: Use itemSnapshotList instead of iterating through indices
Tff7b72val Te6edf3remoteMessageCount Tff7b72by
Te6edf3rememberTb4b4b4(Te6edf3messagesTb4b4b4.Te6edf3itemCountTb4b4b4) Tb4b4b4{ Te6edf3derivedStateOf Tb4b4b4{ Te6edf3messagesTb4b4b4.Te6edf3itemSnapshotListTb4b4b4.Te6edf3itemsTb4b4b4.Te6edf3count Tb4b4b4{ Tff7b72!Tffa657itTb4b4b4.Te6edf3fromLocal Tb4b4b4} Tb4b4b4} Tb4b4b4}

T8b949e// Mark messages as read after debounce period
T8b949e// Handles both scrolling cases and when all unread messages are visible without scrolling
T8b949e// Effect restarts when isResumed changes, so returning from background will restart the debounce
Te6edf3LaunchedEffectTb4b4b4(Te6edf3remoteMessageCountTb4b4b4, Te6edf3listStateTb4b4b4, Te6edf3isResumedTb4b4b4) Tb4b4b4{
Te6edf3snapshotFlow Tb4b4b4{
T8b949e// Emit when scroll stops OR when at initial position (covers no-scroll case)
T8b949e// Include isResumed in the snapshot so lifecycle changes trigger new emissions
Tff7b72if Tb4b4b4(Te6edf3listStateTb4b4b4.Te6edf3isScrollInProgress Tff7b72|Tff7b72| Tff7b72!Te6edf3isResumedTb4b4b4) Tb4b4b4{
Tff7b72null T8b949e// Scrolling in progress or not resumed, don't emit
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3listStateTb4b4b4.Te6edf3firstVisibleItemIndex T8b949e// Emit current position when not scrolling and resumed
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3debounceTb4b4b4(Te6edf3timeoutMillis Tff7b72= Te6edf3UnreadUiDefaultsTb4b4b4.Te6edf3SCROLL_DEBOUNCE_MILLISTb4b4b4)
Tb4b4b4.Te6edf3collectLatest Tb4b4b4{ Te6edf3index Tff7b72-Tff7b72>
T8b949e// Only mark messages as read if we have a valid index (screen is visible and not scrolling)
Tff7b72if Tb4b4b4(Te6edf3index Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3lastUnreadIndex Tff7b72= Te6edf3findLastUnreadMessageIndexTb4b4b4(Te6edf3messagesTb4b4b4)
T8b949e// If we're at/past the oldest unread, mark the first visible unread message
T8b949e// Since newer messages have HIGHER timestamps, marking a newer message's timestamp
T8b949e// will batch-mark all older messages via SQL: WHERE received_time <= timestamp
Tff7b72if Tb4b4b4(Te6edf3lastUnreadIndex Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3index Tff7b72<Tff7b72= Te6edf3lastUnreadIndexTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3firstVisibleUnread Tff7b72= Te6edf3findFirstVisibleUnreadMessageTb4b4b4(Te6edf3messagesTb4b4b4, Te6edf3indexTb4b4b4)
Te6edf3firstVisibleUnreadTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3currentOnUnreadChangeTb4b4b4(Tffa657itTb4b4b4.Te6edf3uuidTb4b4b4, Tffa657itTb4b4b4.Te6edf3receivedTimeTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.12s